mod verifier;

use std::path::Path;
use base64::{Engine};
use base64::prelude::BASE64_STANDARD;
use serde::{Deserialize, Serialize};
use crate::verifier::ItrusteeVerifier;

#[tokio::main]
async fn main() {
    println!("Hello, world!");
    let _user_data = EvidenceRequest {
        uuid: "".to_string(),
        challenge: vec![],
        ima: None,
    };
    let evidence = ItrusteeAttester::default()
        .tee_get_evidence(_user_data.clone())
        .await;
    println!("evidence: {}", evidence);
    let user_u8 = bincode::serialize(&_user_data).unwrap();
    let data = ItrusteeVerifier::default().evaluate(&user_u8, &evidence.as_bytes()).await.unwrap();
    println!("data: {:?}", data);
}

#[derive(Debug, Default)]
pub struct ItrusteeAttester {}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvidenceRequest {
    pub uuid: String,
    pub challenge: Vec<u8>,
    pub ima: Option<bool>,
}

impl ItrusteeAttester {
    pub async fn tee_get_evidence(&self, user_data: EvidenceRequest) -> String {
        let ret = itrustee_provision();
        if ret.is_err() {
            log::error!("itrustee attester provision failed");
        }

        itrustee_get_evidence(user_data)
    }
}

pub fn detect_platform() -> bool {
    Path::new("/usr/bin/tee").exists()
}

#[derive(Serialize, Deserialize)]
struct ReportInputPayload {
    version: String,
    nonce: String,
    uuid: String,
    hash_alg: String,
    with_tcb: bool,
    request_key: bool,
}

#[derive(Serialize, Deserialize)]
struct ItrusteeInput {
    handler: String,
    payload: ReportInputPayload,
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ra_buffer_data {
    pub size: ::std::os::raw::c_uint,
    pub buf: *mut ::std::os::raw::c_uchar,
}

#[link(name = "qca")]
unsafe extern "C" {
    pub fn RemoteAttest(
        in_: *mut ra_buffer_data,
        out: *mut ra_buffer_data,
    ) -> ::std::os::raw::c_uint;
}

const MAX_CHALLENGE_LEN: usize = 64;
fn itrustee_get_evidence(user_data: EvidenceRequest) -> String {
    let challenge = BASE64_STANDARD.decode(&user_data.challenge).unwrap();
    let len = challenge.len();
    if len <= 0 || len > MAX_CHALLENGE_LEN {
        log::error!(
            "challenge len is error, expecting 0 < len <= {}, got {}",
            MAX_CHALLENGE_LEN,
            len
        );
    }
    let payload = ReportInputPayload {
        nonce: String::from_utf8(user_data.challenge).unwrap(),
        uuid: user_data.uuid,
        with_tcb: false,
        request_key: true,
        version: String::from("TEE.RA.1.0"),
        hash_alg: String::from("HS256"),
    };

    let itrustee_input: ItrusteeInput = ItrusteeInput {
        handler: String::from("report-input"),
        payload: payload,
    };
    let mut buf = serde_json::to_string(&itrustee_input).unwrap();
    let mut input = ra_buffer_data {
        size: buf.len() as ::std::os::raw::c_uint,
        buf: buf.as_mut_ptr() as *mut ::std::os::raw::c_uchar,
    };

    let mut report = Vec::new();
    report.resize(0x3000, b'\0');
    let mut output = ra_buffer_data {
        size: report.len() as ::std::os::raw::c_uint,
        buf: report.as_mut_ptr() as *mut ::std::os::raw::c_uchar,
    };

    unsafe {
        let ret = RemoteAttest(&mut input, &mut output);
        if ret != 0 {
            log::error!("itrustee get report failed, ret:{}", ret);
        }
        let out_len: usize = output.size.try_into().unwrap();
        report.set_len(out_len);
    }
     String::from_utf8(report).unwrap()
}

fn itrustee_provision() -> Result<(), Box<dyn std::error::Error>> {
    let json = r#"{"handler":"provisioning-input","payload":{"version":"TEE.RA.1.0","scenario":"sce_no_as","hash_alg":"HS256"}}"#;

    let provision_input: serde_json::Value = serde_json::from_str(json)?;
    let mut provision_input = provision_input.to_string();

    let mut input = ra_buffer_data {
        size: provision_input.len() as ::std::os::raw::c_uint,
        buf: provision_input.as_mut_ptr() as *mut ::std::os::raw::c_uchar,
    };

    let mut report = Vec::new();
    report.resize(0x3000, b'\0');

    let mut output = ra_buffer_data {
        size: report.len() as ::std::os::raw::c_uint,
        buf: report.as_mut_ptr() as *mut ::std::os::raw::c_uchar,
    };
    unsafe {
        let ret = RemoteAttest(&mut input, &mut output);
        if ret != 0 {
            println!("itrustee get report failed, ret:{}", ret);
        }
    }
    Ok(())
}

